#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
@author: liang kang
@contact: gangkanli1219@gmail.com
@time: 2018/5/24 17:58
@desc: 
"""
import itkExtras
import numpy as np
from skimage import measure

from utils.config import Number
from utils.file.image_io import read_dicom
from utils.file.xml_reader import XmlDecoder
from utils.functions import check_spacing, get_frame_instance
from utils.functions import merge_dict


class NoduleReader(object):

    def __init__(self, dcm, xml):
        self.dcm = dcm
        self.xml = xml
        image, self.dcm_io = read_dicom(str(dcm))
        image_array = itkExtras.GetArrayFromImage(image)
        self.shape = image_array.shape
        spacing_img = [image.GetSpacing()[0],
                       image.GetSpacing()[1],
                       image.GetSpacing()[2]]
        spacing_io = self.dcm_io.GetValueFromTag('0028|0030', '')[1].split('\\')
        spacing_io.append(self.dcm_io.GetValueFromTag('0018|0050', '')[1])
        self.spacing = check_spacing(spacing_img, spacing_io)

        self.nodule_list = None
        self.region = None
        self.break_scan = False
        self.msg = ''

    def _get_nodule_list(self):
        first_instance = int(self.dcm_io.GetValueFromTag('0020|0013', '')[1])
        series_id = self.dcm_io.GetValueFromTag('0020|000E', '')[1]
        patient_id = self.dcm_io.GetValueFromTag('0010|0020', '')[1]
        frame_instance = get_frame_instance(self.dcm, first_instance)
        if frame_instance is None:
            self.msg = 'Broken Scan'
            self.break_scan = True
            return
        if self.spacing is None or self.spacing[2] <= 0 or \
                self.spacing[2] > Number.SPACING:
            self.msg = 'Spacing is {}'.format(self.spacing)
            self.break_scan = True
            return
        reader = XmlDecoder(self.xml, frame_instance, patient_id, series_id)
        self.nodule_list = reader.update()
        if 0 == len(self.nodule_list):
            self.msg = 'No Nodules'
            self.break_scan = True
            return

    def _reset_nodule_list(self):
        mask = np.zeros(self.shape, dtype=np.bool)
        for nodule in self.nodule_list:
            for roi in nodule.roi_list:
                mask[np.split(roi.edge_maps, 3, 1)] = 1
        label = measure.label(mask)
        self.regions = measure.regionprops(label)

        for idx, nodule in enumerate(self.nodule_list.copy()):
            center = nodule.get_center()
            min_dis, index = 1e8, 0
            for i, r in enumerate(self.regions):
                this_dis = np.linalg.norm(center - np.asarray(r.centroid))
                if min_dis > this_dis:
                    min_dis, index = this_dis, i
            self.nodule_list[idx].index_id = index

    def _merge_nodule_list(self):

        def _merge_nodule(nodules):
            if 1 == len(nodules):
                return nodules[0]
            nodule = nodules[0]
            for n in nodules[1:]:
                nodule.merge_nodule(n)
            return nodule

        nodule_pool = merge_dict(*[{str(nodule.index_id): [nodule]}
                                   for nodule in self.nodule_list])
        self.nodule_list = [_merge_nodule(value)
                            for _, value in nodule_pool.items()]
        for idx, nodule in enumerate(self.nodule_list.copy()):
            region = self.regions[self.nodule_list[idx].index_id]
            self.nodule_list[idx].roi_list.clear()
            self.nodule_list[idx].points = region.coords
            self.nodule_list[idx].diameter = (
                    region.equivalent_diameter * np.power(np.prod(self.spacing),
                                                          1 / 3))
            self.nodule_list[idx].bbox = region.bbox
            self.nodule_list[idx].centroid = np.asarray(region.centroid)

    def update(self):
        self._get_nodule_list()
        if self.break_scan:
            return
        self._reset_nodule_list()
        self._merge_nodule_list()
        return
